package gang.org.springframework.framework.support;

import gang.org.springframework.framework.bean.GangBeanWrapper;
import gang.org.springframework.framework.bean.GangBeanWrapperIml;
import gang.org.springframework.framework.bean.GangRootBeanDefinition;
import gang.org.springframework.framework.bean.GangTypeConverter;
import gang.org.springframework.framework.core.GangParameterNameDiscoverer;
import gang.org.springframework.framework.factory.GangAbstractAutowireCapableBeanFactory;
import gang.org.springframework.framework.factory.GangConstructorArgumentValues;
import gang.org.springframework.framework.factory.config.GangAutowireCapableBeanFactory;
import gang.org.springframework.framework.factory.config.GangDependencyDescriptor;
import gang.org.springframework.framework.util.GangClassUtils;
import lombok.SneakyThrows;

import java.lang.reflect.Constructor;
import java.lang.reflect.Executable;
import java.lang.reflect.Method;
import java.util.*;

public class GangConstructorResolver {

    public static final Object autowiredArgumentMarker = new Object();

    public final GangAbstractAutowireCapableBeanFactory beanFactory;

    public final Object logger;


    public GangConstructorResolver(GangAbstractAutowireCapableBeanFactory beanFactory) {
        this.beanFactory = beanFactory;
        //TODO
        this.logger = new Object();
    }

    /**
     * 业务意义
     * 创建实例bean【tomcatServletWebServerFactory】 该实例bean【tomcatServletWebServerFactory】为一个方法bean，该方法bean由工厂类$EmbeddedTomcat创建
     * @param beanName, value = tomcatServletWebServerFactory
     * @param mbd, value = TomcatServletWebServerFactory@xxx
     * @param explicitArgs, value = null
     * */
    public GangBeanWrapper instantiatedUsingFactoryMethod(String beanName, GangRootBeanDefinition mbd, Object[] explicitArgs){

        //TODO
        GangBeanWrapperIml bw = new GangBeanWrapperIml();

        //TODO
        //this.beanFactory.initBeanWrapper(bw)

        Object factoryBean = null;
        Class<?> factoryClass;
        boolean isStatic;

        //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
        //  场景处理一
        //
        //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
        String factoryBeanName = mbd.getFactoryBeanName();
        if (factoryBeanName != null) {
            factoryBean = this.beanFactory.getBean(factoryBeanName);
            factoryClass = factoryBean.getClass();
            isStatic = false;
        }else{
            //TODO
            factoryClass = mbd.getBeanClass();
        }

        //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
        //  场景处理二
        //
        //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
        Method factoryMethodToUse = null;
        GangArgumentsHolder argsHolderToUse = null;
        Object[] argsToUse = null;
        if (explicitArgs != null) {
            //TODO
        }else {
            //TODO
        }

        //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
        //  场景处理三
        //
        //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
        if (factoryMethodToUse == null || argsToUse == null) {
            factoryClass = GangClassUtils.getUserClass(factoryClass);
            List<Method> candidates = null;

            //条件1 -> if (mbd.isFactoryMethodUnique)
            if(mbd.isFactoryMethodUnique){
                if (factoryMethodToUse == null) {
                    factoryMethodToUse = mbd.getResolvedFactoryMethod();
                }
                if (factoryMethodToUse != null) {
                    candidates = Collections.singletonList(factoryMethodToUse);
                }
            }

            //条件2 -> if (candidates == null)
            if (candidates == null){
                //TODO
            }
            //条件3 -> if( candidates.size() == 1)
            if(candidates.size() == 1){

                Method uniqueCandidate = candidates.get(0);

                if (uniqueCandidate.getParameterCount() == 0) {
                    // TODO
                    // mbd.factoryMethodToIntrospect=
                    // mbd.resolvedConstructorOrFactoryMethod =
                    // mbd.constructorArgumentResolved =
                    // mbd.resolvedConstructorArguments =
                    bw.setBeanInstance(instantiate(beanName, mbd, factoryBean, uniqueCandidate, argsToUse));
                    return bw;
                }
            }

            //条件4 -> if ( candidates.size() > 1)
            if (candidates.size() > 1) {
                //TODO
            }

            //条件5 -> if ( explicitArgs != null)
            GangConstructorArgumentValues resolvedValues = null;
            Set<Method> ambiguousFactoryMethods =  null;
            int minNrOfArgs;
            if (explicitArgs != null) {
                //TODO
                minNrOfArgs = 0;
            }
            else {
                if (mbd.hasConstructorArgumentValues()) {
                    //TODO
                    minNrOfArgs = 0;
                }else {
                    minNrOfArgs = 0;
                }
            }

            //循环loop
            for (Method candidate : candidates) {
                int parameterCount = candidate.getParameterCount();

                if (parameterCount >= minNrOfArgs) {

                    GangArgumentsHolder argsHolder;

                    Class<?>[] parameterTypes = candidate.getParameterTypes();

                    if (explicitArgs != null) {
                        argsHolder = null;
                        //TODO
                    }else {
                        String[] paramNames = null;
                        GangParameterNameDiscoverer pnd = this.beanFactory.getParameterNameDiscoverer();
                        if (pnd != null) {
                            paramNames = pnd.getParameterNames(candidate);
                        }
                        argsHolder = createArgumentArray(beanName, mbd, resolvedValues, bw, parameterTypes, paramNames, candidate, true, true);
                    }

                    //TODO
                    //int typeDiffWeight =

                    //TODO
                    if (true){
                        factoryMethodToUse = candidate;
                        argsHolderToUse = argsHolder;
                        argsToUse = argsHolder.arguments;
                        // TODO
                        // minTypeDiffWeight = typeDiffWeight;
                        // ambiguousFactoryMethods = null;
                    }

                }
            }

            //条件6 -> if(factoryMethodToUse == null || argsToUse == null)
            if (factoryMethodToUse ==null || argsToUse == null){
                //TODO
            }

            //条件7 -> else if (void.class == factoryMethodToUse.getReturnType())
            else if (void.class == factoryMethodToUse.getReturnType()){
                //TODO
            }
            //条件8 -> else if ( ambiguousFactoryMethods != null)
            else if (ambiguousFactoryMethods != null){
                //TODO
            }

            //条件9 -> if( explicitArgs == null && argsHolderToUser != null)
            if (explicitArgs == null && argsHolderToUse != null){
                //TODO
            }
        }

        bw.setBeanInstance(instantiate(beanName, mbd,factoryBean,factoryMethodToUse, argsToUse));
        return bw;
    }

    /**
     * 业务意义
     * 1）实例化一个Bean对象
     * */
    public Object instantiate(String beanName, GangRootBeanDefinition mbd, Object factoryBean, Method factoryMethod, Object[] args){

        try{
            if (false) {
                //TODO
            }else {
                return this.beanFactory.getInstantiationStrategy().instantiate(mbd, beanName, this.beanFactory, factoryBean, factoryMethod, args);
            }
        }
        catch (Exception e){
            e.printStackTrace();
        }

        return null;
    }

    public GangBeanWrapper autowireConstructor(String beanName,
                                               GangRootBeanDefinition mbd,
                                               Constructor<?>[] choseCtors,
                                               Object[] explicitArgs){

        GangBeanWrapperIml bw = new GangBeanWrapperIml();
        beanFactory.initBeanWrapper(bw);

        Constructor<?> constructorToUse = null;
        GangArgumentsHolder argumentsHolderToUse = null;
        Object[] argsToUse = null;

        if (explicitArgs != null) {
            //TODO
        }else{
            Object[] argsToResolve = null;
            synchronized (mbd.constructorArgumentLock){
                //TODO
            }
            if (argsToResolve != null) {
                //TDDO
            }
        }

        if (constructorToUse == null ||argsToUse == null) {
            Constructor<?>[] candidates = choseCtors;

            if (candidates == null) {
                //TODO
            }
            if (candidates.length == 1 && explicitArgs == null && !mbd.hasConstructorArgumentValues()) {
                Constructor<?> uniqueCandidate = candidates[0];
                if (uniqueCandidate.getParameterCount() == 0) {
                    //TODO
                }
            }
            boolean autowiring = choseCtors != null || mbd.getResolvedAutowireMode() == GangAutowireCapableBeanFactory.autowire_constructor;

            GangConstructorArgumentValues resolvedValue = null;

            int minNrOfArgs = 0;
            if (explicitArgs !=null) {
                //TODO
            }else {
                GangConstructorArgumentValues constructorArgumentValues = mbd.getConstructorArgumentValues();
                resolvedValue = new GangConstructorArgumentValues();
                minNrOfArgs = 0;
            }

            //TODO SET NULL
            int minTypeDiffWgith = Integer.MAX_VALUE;


            for (Constructor<?> candidate : candidates) {
                int parameterCount = candidate.getParameterCount();

                if (constructorToUse != null && argsToUse != null && argsToUse.length > parameterCount) {
                    //TODO
                }

                if(parameterCount < minNrOfArgs){
                    //TODO
                }

                GangArgumentsHolder argsHolder = null;
                Class<?>[] parameterTypes = candidate.getParameterTypes();
                if (resolvedValue != null) {
                    try {
                        String[] paramNames = null; //TODO
                        if (paramNames == null) {
                            GangParameterNameDiscoverer pdn = this.beanFactory.getParameterNameDiscoverer();
                            if (pdn != null) {
                                paramNames = pdn.getParameterNames(candidate);
                            }
                        }
                        //构造参数，初始化完成
                        argsHolder = createArgumentArray(beanName, mbd, resolvedValue, bw, parameterTypes, paramNames, getUserDeclaredConstructor(candidate), true, true);
                    }catch (Exception e){
                        e.printStackTrace();
                    }
                }else {
                    //TODO
                }

                int typeDiffWeight = -1021;
                if (typeDiffWeight < minTypeDiffWgith){
                    constructorToUse = candidate;
                    argumentsHolderToUse = argsHolder;
                    argsToUse = argsHolder.arguments;
                }

            }

            if (false){
                //TODO
            }
            else if (false){
                //TODO
            }
            if (false){
                //TODO
            }
        }
        Object instantiate = instantiate(beanName, mbd, constructorToUse, argsToUse);
        bw.setBeanInstance(instantiate);
        return bw;
    }

    @SneakyThrows
    public Object instantiate(String beanName,
                              GangRootBeanDefinition mbd,
                              Constructor<?> constructorToUse,
                              Object[] argsToUse){
        try {
            GangInstantiationStrategy strategy = this.beanFactory.getInstantiationStrategy();//CGLIB
            if (false){
                return null;
            }else {
                return strategy.instantiate(mbd, beanName, this.beanFactory, constructorToUse, argsToUse);
            }
        }catch (Exception e){
            e.printStackTrace();
            throw new Exception();
        }
    }

    public static class GangArgumentsHolder{

        public final Object[] rawArgements;

        public final Object[] arguments;

        public final Object[] preparedArguments;

        public boolean resolveNecessary = false;

        public  GangArgumentsHolder(int size){
            this.rawArgements = new Object[size];
            this.arguments = new Object[size];
            this.preparedArguments = new Object[size];
        }

    }

    /**
     * 业务意义：构造参数实例
     * @param beanName spring bean name
     * @param mbd spring bean definition
     * @param resolvedValues TODO
     * @param  bw spring bean wrapper
     * @param paramNames spring bean class constructor parameter name
     * @param paramTypes spring bean class constructor parameter type
     * @param executable spring bean class constructor
     * */
    public GangArgumentsHolder createArgumentArray(String beanName,
                                                   GangRootBeanDefinition mbd,
                                                   GangConstructorArgumentValues resolvedValues,
                                                   GangBeanWrapper bw,
                                                   Class<?>[] paramTypes,
                                                   String[] paramNames,
                                                   Executable executable,
                                                   boolean autowiring,
                                                   boolean fallback){

        //TODO
        GangTypeConverter customConverter = null;

        //TODO
        GangTypeConverter converter = customConverter != null ? customConverter : bw;

        //TODO
        GangArgumentsHolder args = new GangArgumentsHolder(paramTypes.length);

        Set<GangConstructorArgumentValues.GangValueHolder> usedValueHolders = new HashSet<>(paramTypes.length);

        /*
         * 1 resourceProperties
         * 2 mvcProperties
         * 3 webProperties
         * 4 beanFactory
         * */
        Set<String> autowiredBeanNames = new LinkedHashSet<>(4);

        for (int i = 0; i < paramTypes.length; i++) {

            Class<?> paramType = paramTypes[i];
            String paramName = paramNames != null ? paramNames[i] : "";

            GangConstructorArgumentValues.GangValueHolder valueHolder = null;

            if (resolvedValues != null) {
               valueHolder = resolvedValues.getArgumentValue(i, paramType, paramName, usedValueHolders);

               if (false){
                   //TODO
               }
            }
            if (valueHolder != null) {
                //TODO
            }else {
                GangMethodParameter methodparam = GangMethodParameter.forExecutable(executable, i);
                if (!autowiring){
                    //TODO
                }
                try {
                    Object autowiredArgument = resolveAutowiredArgument(methodparam, beanName, autowiredBeanNames, converter, true);
                    args.rawArgements[i] = autowiredArgument;
                    args.arguments[i] = autowiredArgument;
                    args.preparedArguments[i] = autowiredArgumentMarker;
                    args.resolveNecessary = true;

                }catch (Exception e){
                    e.printStackTrace();
                }

            }

        }

        for (String autowiredBeanName : autowiredBeanNames) {
            this.beanFactory.registerDependentBean(autowiredBeanName, beanName);
        }


        return args;
    }

    public Constructor<?> getUserDeclaredConstructor(Constructor<?> constructor){
        return constructor;
    }

    /**
     * 每个构造方法中，每个参数单独处理
     * @param param 对构造方法中，每一个参数的二次封装
     * @param beanName spring bean 的名字
     * @param autowiredBeanNames TODO
     * @param typeConverter spring bean 包装类对象
     * @param fallback TOOD
     * */
    public Object resolveAutowiredArgument(GangMethodParameter param,
                                           String beanName,
                                           Set<String> autowiredBeanNames,
                                           GangTypeConverter typeConverter,
                                           boolean fallback){


        Class<?> paramType = param.getParameterType(); //获取到构造方法中的对像，例如：ResourcePeoperties

        //if (InjectionPoint.class.isAssignableFrom(paramType)) {
        if (false){
            //TODO
        }
        try {
            return this.beanFactory.resolveDependency(new GangDependencyDescriptor(param, true), beanName, autowiredBeanNames, typeConverter);
        }catch (Exception e){
            e.printStackTrace();
        }

        return null;
    }


}
